package edu.csu.smartpark.service.Impl;

import edu.csu.smartpark.dao.ParkingOrderDAO;
import edu.csu.smartpark.dao.TollMonthCardOrderDAO;
import edu.csu.smartpark.model.DO.*;
import edu.csu.smartpark.model.PO.ParkingOrderPO;
import edu.csu.smartpark.model.PO.TollMonthCardOrderPO;
import edu.csu.smartpark.model.common.BusinessException;
import edu.csu.smartpark.util.CommonUtil;
import edu.csu.smartpark.util.Constants;
import edu.csu.smartpark.util.RequestUtil;
import edu.csu.smartpark.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
public class OrderServiceImpl implements edu.csu.smartpark.service.OrderService {
    @Autowired
    private ParkingOrderDAO parkingOrderDAO;

    @Autowired
    private TollMonthCardOrderDAO tollMonthCardOrderDAO;

    @Override
    public String createParkingOrder(ParkingOrderDO parkingOrderDO) throws BusinessException {
        if (parkingOrderDO == null){
            log.warn("[OrderService] create parking order fail , because parking order DO is null");
            throw new BusinessException(BusinessException.PARAMETERERROR, "parking order is empty");
        }
        ParkingOrderPO parkingOrderPO = new ParkingOrderPO();
        BeanUtils.copyProperties(parkingOrderDO, parkingOrderPO);
        int row = parkingOrderDAO.insert(parkingOrderPO);
        return row > 0 ? parkingOrderPO.getId(): "";
    }

    @Override
    public void updateParkingOrder(ParkingOrderDO parkingOrderDO) throws BusinessException {
        if (parkingOrderDO == null || parkingOrderDO.getId().isEmpty()){
            log.warn("[OrderService] update parking order fail, because id or parking order is null");
            return;
        }
        ParkingOrderPO parkingOrderPO = new ParkingOrderPO();
        BeanUtils.copyProperties(parkingOrderDO, parkingOrderPO);
        parkingOrderDAO.updateById(parkingOrderPO);
    }

    @Override
    public List<ParkingOrderDO> getParkingOrder(ParkingOrderQueryDO parkingOrderQueryDO) throws BusinessException {
        return null;
    }

    @Override
    /*
    * @Description: 传入本地生成的停车收费订单，调用支付平台生成支付订单，并返回支付订单相关的签名等参数，供前端SDK调用支付时使用
    * @Author: LZY
    * @Date: 2021/6/27 11:35
    * @Params: [parkingOrderDO]
    * @Return: edu.csu.smartpark.model.DO.PayOrderCommitResultDO
    */
    public PayOrderCommitResultDO commitParkingPayOrder(ParkingOrderDO parkingOrderDO) throws BusinessException {
        if (parkingOrderDO == null){
            log.warn("[OrderService] commit parking pay order fail, because parking order is empty");
            throw new BusinessException(BusinessException.PARAMETERERROR, "parking order is empty");
        }
        PayOrderDO payOrderDO = new PayOrderDO();
        BeanUtils.copyProperties(parkingOrderDO, payOrderDO);
        payOrderDO.setMchOrderNo(parkingOrderDO.getId());
        payOrderDO.setAmount(parkingOrderDO.getPayableAmount());
        payOrderDO.setCurrency("cny");
        // TODO 回调接口，有待完善
        payOrderDO.setNotifyUrl("/pay_result/callback");
        payOrderDO.setSubject("parking fee");
        payOrderDO.setBody("parking fee");
        Map<String, Object> params = makePayOrderCreateParams(payOrderDO);
        // TODO url待完善
        String response = RequestUtil.doPost("url", params);
        return makePayOrderCreateResponse(response);
    }

    @Override
    /*
    * @Description: 轮询支付平台的订单状态查询接口，判定订单是否已支付。轮询间隔暂定3s一次
    * @Author: LZY
    * @Date: 2021/6/27 11:36
    * @Params: [orderId]
    * @Return: boolean
    */
    public boolean isParkingOrderPay(String orderId) throws BusinessException {
        /*
        // 1. 查询本地停车订单状态是否已支付
        ParkingOrderPO parkingOrderPO = parkingOrderDAO.selectById(orderId);
        if (parkingOrderPO == null){
            return false;
        }
        // 该订单已支付失败，可以直接返回false
        if (parkingOrderPO.getStatus() == -1){
            return false;
        }
        if (parkingOrderPO.getStatus() == 2){
            // 订单已支付，直接返回true
            return true;
        }

        // 2. 主动请求支付系统查询订单状态，如果支付成功，使用乐观锁更新本地停车订单状态。
        String response = RequestUtil.doPost("https://139.9.104.172:9216/api/pay/query", makePayOrderQueryParams(orderId));
        PayOrderDO payOrderDO = makePayOrderQueryResponse(response);
        if (payOrderDO == null){
            return false;
        }
        ParkingOrderDO parkingOrderDO = new ParkingOrderDO();
        parkingOrderDO.setId(parkingOrderPO.getId());

        int orderState = payOrderDO.getState();
        List<Integer> failState = new LinkedList<Integer>(){{
            add(3);add(4);add(5);add(6);
        }};
        if (failState.contains(orderState)){
            // 订支付订单已经支付失败
            parkingOrderDO.setStatus(-1);
            updateParkingOrder(parkingOrderDO);
            return false;
        }

        if (payOrderDO.getState() == 2){
            parkingOrderDO.setStatus(2);
            updateParkingOrder(parkingOrderDO);
            return true;
        }

        return false;

         */
        // TODO 支付现在还未接入，方便测试默认返回已支付
        return true;

    }

    @Override
    public String createMonthCardOrder(TollMonthCardOrderDO tollMonthCardOrderDO) throws BusinessException {
        if (tollMonthCardOrderDO == null){
            log.warn("[OrderService] create toll month order fail , because toll month order DO is null");
            throw new BusinessException(BusinessException.PARAMETERERROR, "toll month order is empty");
        }
        TollMonthCardOrderPO tollMonthCardOrderPO = new TollMonthCardOrderPO();
        BeanUtils.copyProperties(tollMonthCardOrderDO, tollMonthCardOrderPO);
        int row = tollMonthCardOrderDAO.insert(tollMonthCardOrderPO);
        return row > 0 ? tollMonthCardOrderPO.getId(): "";
    }

    @Override
    public void updateMonthCardOrder(TollMonthCardOrderDO tollMonthCardOrderDO) throws BusinessException {

    }

    @Override
    public List<TollMonthCardOrderDO> getMonthCardOrder(TollMonthCardOrderQueryDO tollMonthCardOrderQueryDO) throws BusinessException {
        return null;
    }

    @Override
    public PayOrderCommitResultDO commitMonthCardPayOrder(TollMonthCardOrderDO tollMonthCardOrderDO) throws BusinessException {
        if (tollMonthCardOrderDO == null){
            log.warn("[OrderService] commit toll month pay order fail, because parking order is empty");
            throw new BusinessException(BusinessException.PARAMETERERROR, "toll month pay order is empty");
        }
        PayOrderDO payOrderDO = new PayOrderDO();
        BeanUtils.copyProperties(tollMonthCardOrderDO, payOrderDO);
        payOrderDO.setMchOrderNo(tollMonthCardOrderDO.getId());
        payOrderDO.setAmount(tollMonthCardOrderDO.getPayableAmount());
        payOrderDO.setCurrency("cny");
        // TODO 回调接口，有待完善
        payOrderDO.setNotifyUrl("/pay_result/callback");
        payOrderDO.setSubject("month card");
        payOrderDO.setBody("month card"+tollMonthCardOrderDO.getTollMonthCardId());
        Map<String, Object> params = makePayOrderCreateParams(payOrderDO);
        // TODO url待完善
        String response = RequestUtil.doPost("url", params);
        return makePayOrderCreateResponse(response);
    }

    private Map<String, Object> makePayOrderQueryParams(String mchOrderNo){
        Map<String, Object> params = new HashMap<>();
        // 商户号
        params.put("mchNo", Constants.MchNo);
        params.put("appId", Constants.AppId);
        params.put("mchOrderNo", mchOrderNo);
        params.put("reqTime", System.currentTimeMillis());
        params.put("version", Constants.Version);
        String sign = createPaySign(params);
        params.put("sign", sign.toUpperCase());
        params.put("signType", Constants.SignType);
        return params;
    }

    private PayOrderDO makePayOrderQueryResponse(String response){
        Map<String, Object> paramMap = StringUtil.parseJsonString(response);
        if (paramMap == null){
            return new PayOrderDO();
        }
        Object data = paramMap.get("data");
        if (data == null){
            return new PayOrderDO();
        }
        PayOrderDO payOrderDO = new PayOrderDO();
        BeanUtils.copyProperties(data, payOrderDO);
        Date creatTime = new Date((long)paramMap.get("createdAt"));
        payOrderDO.setCreateTime(creatTime);
        long successTimeStamp = (long)paramMap.get("successTime");
        if (successTimeStamp > 0){
            Date successTime = new Date(successTimeStamp);
            payOrderDO.setSuccessTime(successTime);
        }
        payOrderDO.setSign((String) paramMap.get("sign"));
        return payOrderDO;
    }

    private PayOrderCommitResultDO makePayOrderCreateResponse(String response){
        Map<String, Object> paramMap = StringUtil.parseJsonString(response);
        if (paramMap == null){
            return new PayOrderCommitResultDO();
        }
        Object data = paramMap.get("data");
        if (data == null){
            return new PayOrderCommitResultDO();
        }
        PayOrderCommitResultDO payOrderCommitResultDO = new PayOrderCommitResultDO();
        BeanUtils.copyProperties(data, payOrderCommitResultDO);
        payOrderCommitResultDO.setSign((String) paramMap.get("sign"));
        return payOrderCommitResultDO;
    }
    private Map<String, Object> makePayOrderCreateParams(PayOrderDO payOrderDO){
        Map<String, Object> params = new HashMap<>();
        params.put("mchNo", Constants.MchNo);
        params.put("appId", Constants.AppId);
        params.put("mchOrderNo", payOrderDO.getMchNo());
        params.put("wayCode", payOrderDO.getWayCode());
        params.put("amount", payOrderDO.getAmount());
        params.put("currency", payOrderDO.getCurrency());
        params.put("subject", payOrderDO.getSubject());
        params.put("body", payOrderDO.getBody());
        params.put("notifyUrl", payOrderDO.getNotifyUrl());
        params.put("reqTime", System.currentTimeMillis());
        params.put("version", Constants.Version);
        String sign = createPaySign(params);
        params.put("sign", sign.toUpperCase());
        params.put("signType", Constants.SignType);
        return params;
    }

    private String createPaySign(Map<String, Object> params){
        Collection<String> keySet = params.keySet();
        List<String> keys = new ArrayList<>(keySet);
        Collections.sort(keys);
        StringBuilder paramsStr = new StringBuilder();
        for (int i = 0; i < keys.size(); i++) {
            String value = params.get(keys.get(i)).toString();
            if (value == null || value.isEmpty()){
                continue;
            }
            paramsStr.append(keys.get(i));
            paramsStr.append("=");
            paramsStr.append(value);
            paramsStr.append("&");
        }
        paramsStr.append("key=");
        paramsStr.append(Constants.PrivateKey);
        return CommonUtil.EncryptByMD5(paramsStr.toString());
    }
}
